package com.mspbots.core.wise.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.mspbots.core.AbstractBaseServiceImpl;
import com.mspbots.core.teams.mapper.TeamsUserMapper;
import com.mspbots.core.teams.model.TeamsUser;
import com.mspbots.core.ticketai.mapper.TicketMapper;
import com.mspbots.core.tsheets.SheetsUser;
import com.mspbots.core.tsheets.SheetsUserMapper;
import com.mspbots.core.wise.mapper.*;
import com.mspbots.core.wise.model.*;
import com.mspbots.core.wise.model.dto.TriggerEventDTO;
import com.mspbots.core.wise.service.TenantConfigurationService;
import com.mspbots.core.wise.service.TenantService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitMessagingTemplate;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessagePreparator;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.mail.Address;
import javax.mail.Message;
import javax.mail.internet.InternetAddress;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.mspbots.common.TriggerEvent.NEW_TENANT;

/**
 * @author billb
 */
@Slf4j
@Service
@RequiredArgsConstructor
@CacheConfig(cacheNames = "caching:mspbots:tenants")
public class TenantServiceImpl extends AbstractBaseServiceImpl<TenantMapper, Tenant> implements TenantService {

    private final TenantConfigurationService configurationService;
    private final JavaMailSender mailSender;
    private final RabbitMessagingTemplate messagingTemplate;
    private final StringRedisTemplate stringRedisTemplate;
    private final TicketMapper ticketMapper;
    private final UsersMapper usersMapper;
    private final TenantConfigurationMapper tenantConfigurationMapper;
    private final TeamsUserMapper teamsUserMapper;
    private final TenantUserMapper tenantUserMapper;
    private final TenantUsersMappingMapper tenantUsersMappingMapper;
    private final SheetsUserMapper SheetsUserMapper;


    @Override
    @Cacheable(key = "#root.method.name", unless = "#result==null || #result.empty")
    public List<Tenant> findByStatus() {
        return this.baseMapper.findByStatus();
    }

    @Override
    public List<Tenant> findByAll() {
        List<Tenant> tenants = this.baseMapper.selectList(Wrappers.<Tenant>lambdaQuery().orderByDesc(Tenant::getId));
        return tenants.parallelStream().peek(tenant -> {
            List<TenantConfiguration> configurations = this.configurationService
                    .list(Wrappers.<TenantConfiguration>lambdaQuery()
                            .eq(TenantConfiguration::getTenantId, tenant.getId()));
            tenant.setConfiguration(configurations);
        }).collect(Collectors.toList());
    }

    @Override
    @Cacheable(key = "#root.method.name", unless = "#result==null || #result.empty")
    public List<Tenant> findByActive() {
        List<Tenant> tenants = this.baseMapper.selectList(Wrappers.<Tenant>lambdaQuery()
                .eq(Tenant::getStatusId, 1).orderByDesc(Tenant::getId));
        return tenants.parallelStream().peek(tenant -> {
            List<TenantConfiguration> configurations = this.configurationService
                    .list(Wrappers.<TenantConfiguration>lambdaQuery()
                            .eq(TenantConfiguration::getTenantId, tenant.getId()));
            tenant.setConfiguration(configurations);
        }).collect(Collectors.toList());
    }

    @Override
    public Tenant getById(Serializable id) {
        Tenant tenant = this.baseMapper.selectById(id);
        if (!ObjectUtils.isEmpty(tenant)) {
            List<TenantConfiguration> configurations = this.configurationService.list(
                    Wrappers.<TenantConfiguration>lambdaQuery().eq(TenantConfiguration::getTenantId, tenant.getId()));
            tenant.setConfiguration(configurations);
        }
        return tenant;
    }

    @Override
    @CacheEvict(allEntries = true, beforeInvocation = true)
    @Transactional(rollbackFor = Exception.class)
    public boolean saveFirst(Tenant entity) {
        if (null != entity) {
            boolean result = this.save(entity);
            if (result) {
                log.debug("Create tenant add trigger");

                this.senderMail(entity);

                String entityStr = null;
                try {
                    entityStr = objectMapper.writeValueAsString(entity);
                    JsonNode payload = objectMapper.readTree(entityStr);
                    TriggerEventDTO triggerEventDTO = new TriggerEventDTO();
                    triggerEventDTO.setEventName(NEW_TENANT.getEvent());
                    triggerEventDTO.setScope(NEW_TENANT.getScope());
                    triggerEventDTO.setPayload(payload);
                    this.messagingTemplate.convertAndSend("msp.trigger.event", "", triggerEventDTO);
                    log.info("cw msp.trigger.event NewTenant:[{}] ", this.objectMapper.convertValue(triggerEventDTO, JsonNode.class));
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }
            }

            return result;
        }
        return false;
    }

    @Override
    public boolean deleteTenant(long tenantId) {
        if (ObjectUtils.isEmpty(tenantId)) {
            return false;
        }
        if (tenantId <= 0) {
            return false;
        }
        Map<String, Object> param = Map.of("tenant_id", tenantId);
        log.info("deleteTenant tenantId:{}", tenantId);


        int deleteTenant = this.baseMapper.deleteById(tenantId);
        int deleteConfig = this.tenantConfigurationMapper.deleteByMap(param);
        int deleteTeamsUser = this.teamsUserMapper.deleteByMap(param);
        int deleteTenantUser = this.tenantUserMapper.deleteByMap(param);
        int deleteUsers = this.usersMapper.deleteByMap(param);
        int deleteUserMapping = this.tenantUsersMappingMapper.deleteByMap(param);
        int deleteSheetUser = this.SheetsUserMapper.deleteByMap(param);

        log.info("deleteTenant: {}", deleteTenant);
        log.info("deleteConfig: {}", deleteConfig);
        log.info("deleteTeamsUser: {}", deleteTeamsUser);
        log.info("deleteTenantUser: {}", deleteTenantUser);
        log.info("deleteUsers: {}", deleteUsers);
        log.info("deleteUserMapping: {}", deleteUserMapping);
        log.info("deleteSheetUser: {}", deleteSheetUser);

        int countTenant = this.baseMapper.selectCount(Wrappers.<Tenant>lambdaQuery().eq(Tenant::getId, tenantId));
        int countConfig = this.tenantConfigurationMapper.selectCount(Wrappers.<TenantConfiguration>lambdaQuery().eq(TenantConfiguration::getTenantId, tenantId));
        int countTeamsUser = this.teamsUserMapper.selectCount(Wrappers.<TeamsUser>lambdaQuery().eq(TeamsUser::getTenantId, tenantId));
        int countTenantUser = this.tenantUserMapper.selectCount(Wrappers.<TenantUser>lambdaQuery().eq(TenantUser::getTenantId, tenantId));
        int countUsers = this.usersMapper.selectCount(Wrappers.<Users>lambdaQuery().eq(Users::getTenantId, tenantId));
        int countUserMapping = this.tenantUsersMappingMapper.selectCount(Wrappers.<TenantUserMapping>lambdaQuery().eq(TenantUserMapping::getTenantId, tenantId));
        int countSheetUser = this.SheetsUserMapper.selectCount(Wrappers.<SheetsUser>lambdaQuery().eq(SheetsUser::getTenantId, tenantId));


        log.info("countTenant: {}", countTenant);
        log.info("countConfig: {}", countConfig);
        log.info("countTeamsUser: {}", countTeamsUser);
        log.info("countTenantUser: {}", countTenantUser);
        log.info("countUsers: {}", countUsers);
        log.info("countUserMapping: {}", countUserMapping);
        log.info("countSheetUser: {}", countSheetUser);

        Boolean result = countTenant == 0
                && countConfig == 0
                && countTeamsUser == 0
                && countTenantUser == 0
                && countUsers == 0
                && countUserMapping == 0
                && countSheetUser == 0;

        this.messagingTemplate.convertAndSend("msp.tenant.delete", "", tenantId);

        return result;
    }


    @Override
    public Integer existTenant(String microsoftId) {
        Map<String, Object> columnMap = new HashMap<>();
        columnMap.put("microsoft_id", microsoftId);
        List<Tenant> list = this.baseMapper.selectByMap(columnMap);
        if (CollectionUtils.isEmpty(list)) {
            return 0;
        } else {
            return 1;
        }
    }

    @Override
    public List<Tenant> findByIdsAndActive(List<Long> ids) {
        QueryWrapper<Tenant> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("id", ids);
        List<Tenant> tenants = this.baseMapper.selectList(queryWrapper);
        return tenants.parallelStream().peek(tenant -> {
            List<TenantConfiguration> configurations = this.configurationService
                    .list(Wrappers.<TenantConfiguration>lambdaQuery()
                            .eq(TenantConfiguration::getTenantId, tenant.getId()));
            tenant.setConfiguration(configurations);
        }).collect(Collectors.toList());
    }

    @Override
    public boolean teamsAuthority(Long id, Boolean authority) {
        return update(Wrappers.<Tenant>lambdaUpdate().eq(Tenant::getId, id).set(Tenant::getTeamsAuthority, authority));
    }


    public Boolean senderMail(Tenant entity) {
//        String receiver = "1785319835@qq.com";
        String receiver = "daniel.wang@itsasap.com";
        String receiverCc1 = "ken.lee@jexet.com";
        String receiverCc2 = "teamleads@mspbots.ai";
        String receiverCc3 = "marketing@mspbots.ai";

        MimeMessagePreparator preparator = mimeMessage -> {
            mimeMessage.setRecipient(Message.RecipientType.TO,
                    new InternetAddress(receiver));
            mimeMessage.setRecipients(Message.RecipientType.CC,
                    new Address[]{new InternetAddress(receiverCc1), new InternetAddress(receiverCc2), new InternetAddress(receiverCc3)});
            mimeMessage.setFrom(new InternetAddress("support@mspbots.ai"));
            mimeMessage.setSubject(entity.getMailPostfix() + " sign up");


//            mimeMessage.setContent("Hi Daniel" + "<br><br>" +
//                            "A new tenant sign up.<br> The email is " + entity.getMailPostfix() + "<br><br>"
//                            + "Server:Mspbots.ai",
//                    "text/html;charset=UTF-8");

            mimeMessage.setContent("<br>"
                            + "Server:Mspbots.ai",
                    "text/html;charset=UTF-8");
        };

        try {
            this.mailSender.send(preparator);
        } catch (MailException ex) {
            log.error("mail send error");
            return false;
        }
        return true;
    }
}
